package com.edou.base.mybatis.page;

import java.sql.Connection;
import java.util.Enumeration;
import java.util.Properties;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;

import com.edou.base.mybatis.dialect.Dialect;
import com.edou.base.mybatis.dialect.MySql5Dialect;
import com.edou.base.mybatis.dialect.SqlServerDialect;

/**
 * 分页实现类
 * @author admin
 *
 */
@Intercepts( { @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })
public class PaginationInterceptor implements Interceptor {

	private final static Log logger = LogFactory.getLog(PaginationInterceptor.class);
	private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();  
    private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
    private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();

	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		BoundSql boundSql = statementHandler.getBoundSql();
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler,DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY,DEFAULT_REFLECTOR_FACTORY);
		RowBounds rowBounds = (RowBounds) metaStatementHandler.getValue("delegate.rowBounds");
		if (rowBounds == null || rowBounds == RowBounds.DEFAULT) {
			return invocation.proceed();
		}
		
		//查询sql
		String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
		
		Configuration configuration = (Configuration) metaStatementHandler.getValue("delegate.configuration");
		Dialect.Type databaseType = null;
		String primaryKey = "id";	//sql server主键默认使用id
		try {
			databaseType = Dialect.Type.valueOf(configuration.getVariables().getProperty("dialect").toUpperCase());
			primaryKey = configuration.getVariables().getProperty("primaryKey");
			if(StringUtils.isBlank(primaryKey) && StringUtils.isNotBlank(originalSql)){
				//获取表名
				String[] sqlTempArr = originalSql.toLowerCase().split("\\s");
				int index = ArrayUtils.indexOf(sqlTempArr, "from");
				String tableName = null;
				while (StringUtils.isBlank(tableName = sqlTempArr[index + 1])) {
					index++;
					
					if(index > 100){
						//防止有人的sql代码写的有问题，导致系统死循环
						break;
					}
				}
				
				if(StringUtils.isNotBlank(tableName)){
					//查找table_pk_
					Enumeration<Object> elements = configuration.getVariables().keys();
					while(elements.hasMoreElements()){
						String propKey = elements.nextElement().toString();
						if(propKey.indexOf("table_pk_") == -1){
							continue;
						}
						String compareTable = propKey.substring(propKey.indexOf("table_pk_") + 9).toLowerCase();
						if(StringUtils.equals(tableName, compareTable)){
							//找到该表
							primaryKey = configuration.getVariables().getProperty(propKey);
							break;
						}
					}
				}
				
			}
			
		} catch (Exception e) {
			// ignore
			primaryKey = "id";
		}
		if (databaseType == null) {
			throw new RuntimeException(
					"the value of the dialect property in mybatis config xml is not defined : "
							+ configuration.getVariables().getProperty(
									"dialect"));
		}
		Dialect dialect = null;
		switch (databaseType) {
			case MYSQL:
				dialect = new MySql5Dialect();
				break;
			case SQLSERVER:
				dialect = new SqlServerDialect(boundSql,primaryKey);
				break;
			default:
				throw new Exception("Not Suuport " + databaseType + " Database!");
		}

		
		metaStatementHandler.setValue("delegate.boundSql.sql", dialect.getLimitString(originalSql, rowBounds.getOffset(), rowBounds.getLimit()));
		metaStatementHandler.setValue("delegate.rowBounds.offset",RowBounds.NO_ROW_OFFSET);
		metaStatementHandler.setValue("delegate.rowBounds.limit",RowBounds.NO_ROW_LIMIT);
		if (logger.isDebugEnabled()) {
			logger.debug("生成分页SQL:" + boundSql.getSql());
		}
		return invocation.proceed();
	}

	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	public void setProperties(Properties properties) {
	}

}
